rpc/jsonrpc: fail trace_filter instead of mixing error objects into the result array - #23346
Draft
lupin012 wants to merge 1 commit into
Draft
rpc/jsonrpc: fail trace_filter instead of mixing error objects into the result array#23346lupin012 wants to merge 1 commit into
lupin012 wants to merge 1 commit into
Conversation
…he result array
trace_filter documents its result as Array<TraceEntry>, but filterV3 wrote
per-transaction failures into that array as bare {"error":{...}} items and kept
going, so the JSON-RPC envelope still reported success. Those items carry no
type, action or traceAddress, so a strict decoder breaks on them and a lenient
one silently accepts a partial trace as complete.
Every iterator, lookup, marshal and execution failure now propagates instead.
traceFilterTxn loses its "nil result means the error is already on the stream"
convention and just returns the error, which also drops the nil check at the
call site.
The result array now opens on the first exported trace rather than up front.
That way an error raised before any trace leaves runMethod's LazyFieldStream
unwritten, so the response carries only "error" instead of "result" plus
"error". When traces were already streamed, the handler's ClosePending seals
the half-written array.
Fixes #23128
lupin012
force-pushed
the
lupin012/fix_trace_filter_error_objects
branch
from
August 17, 2026 21:02
6965718 to
a47cae0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #23128
Problem
trace_filterdocuments its result asArray<TraceEntry>, butfilterV3wrote per-transaction failures into that array as bare error objects and kept iterating, so the envelope still reported success:That item has no
type,actionortraceAddress, so a strict decoder breaks on it and a lenient one accepts a partial trace as complete. Thirteen failure paths behaved this way: iteratorNext,HeaderByNumber, missing header,Body, threejson.Marshalcalls,TxnByIdxInBlock,AsMessage, andtraceFilterTxn's timeout, execution,FinalizeTxandCommitBlockpaths — the last four sharing awriteErrclosure.What other clients do
trace_*is not part of execution-apis, which coverseth,debug,engineandtxpoolonly, so there is no official schema to conform to. The de-facto reference is OpenEthereum's trace module, which documents the result as plain "Array — Traces matching given filter", with no error variant among the items.async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>>— the item type is fixed, so a failure can only surface throughRpcResult, i.e. as a JSON-RPC error.ResultWrapper<IEnumerable<ParityTxTraceFromStore>> trace_filter(...)— a block it cannot resolve returnsResultWrapper.Fail, failing the whole request; missing state truncates the stream with a warning. Either way no error item enters the array.Decision
#23128 asked us to choose one explicit result contract out of three:
TraceEntryitems and expose partial failures separately;TraceEntry | TraceItemError, including how clients should detect incomplete results.This PR implements the first one. It is what reth and Nethermind already do, so it aligns us with the de-facto contract instead of inventing a third dialect.
Fix
Every one of those paths now propagates the error instead of writing into the array, and
traceFilterTxndrops both itswriteErrclosure and the(nil, nil)"error already on the stream" convention it relied on.The result array opens on the first exported trace instead of up front.
runMethodwraps the handler's stream in aLazyFieldStream, so a field nobody writes to is never emitted, and an error raised before any trace yields an error-only response. Keeping the array open from the start would instead have paired an emptyresultwith theerror. When traces were already streamed,runMethod'srs.CloseIfOpen()→StackStream.ClosePendingseals the half-written array.Tests
TestFilterSignerReflectsBlockOverridesNumberis inverted: it asserted the error text showed up inside the stream, it now asserts the call fails and the stream stays empty.TestFilterErrorAfterExportedTracesKeepsValidJSONis new and covers the error arriving after traces were streamed. Filtering blocks 1-3 with no address filter exports both empty blocks' reward traces before block 3's EIP-155-protected transaction is rejected by the overridden pre-Spurious-Dragon signer. The envelope is assembled the wayrunMethoddoes it, since sealing the array is the handler's job rather thanfilterV3's; this follows the existingTestTraceBlockErrorAfterWritepattern. It asserts the envelope parses, theresultarray parses (i.e.ClosePendingclosed it), every item hastype, and anyerrorpresent decodes as a string rather than an object.Both were confirmed red against
main— each fails onAn error is expected but got nil— and with that assertion relaxed the new one reproduces the malformed array above.